#include "GameManager.h"
#include "Debug.h"
#include "HUDControl.h"
#include "Ship.h"

#include "eventman\IrrEventManager.h"

#include <vector>
#include <functional>
#include <algorithm>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
using namespace IrrPhysx;

void CGameManager::MainLoop() {
	f32 frameDeltaTime;
	u32 then, now;
	CSceneObject  * obj;

	then  = this->mDevice->getTimer()->getTime();
	
#ifdef _DEBUG
	this->mPhysxManager->setDebugDataVisible(true);
#endif
	while(this->mDevice->run()) { 
		now = this->mDevice->getTimer()->getTime();
		frameDeltaTime = (f32)(now - then) / 1000.f;  // Time is seconds
		then = now;

		if(this->mState == E_STATE::Running) {
#ifdef _DEBUG
			TestUpdate(this, frameDeltaTime);
#endif
			this->mPhysxManager->simulate(frameDeltaTime);
			// master event handlers
			//deselect targets
			if(this->mEventManager->keyClick(KEY_ESCAPE)) {
				if(this->mSelectionList.size()) {
					this->mSelectionList[0]->setTarget(NULL);
					this->mSelectionList.clear();
				}
			}

			// Left click
			if(this->mEventManager->mouseClick(EMB_L)) {
				ISceneNode * t = this->mSceneManager->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(this->mDevice->getCursorControl()->getPosition());
				if(t != NULL) {
					obj = this->getObject(t->getID());
					if(obj != NULL) { // did the user click something?
						if(!this->mSelectionList.size()) { // do we already have a selection? are we targeting?
							switch(obj->getType()) {	
								case E_GAMEOBJECT_TYPE::Ship:
									this->mSelectionList.clear();
									this->mSelectionList.add((CShip *)obj);
									break;
								default:
									break;
							}
						} else { // targeting, do it!
							this->mSelectionList[0]->setTarget(obj);
						}
					}
				}
			}

			this->mPhysxManager->fetchResults();

			// garbage collection holder
			std::vector<CSceneObject *> garbage;

			// iterate through all objects. If its a scene object child, then pass time.
			for(OBJINTMAP::iterator iter = this->mObjectList.begin();iter != this->mObjectList.end();iter++) {
				obj = iter->second;
				obj->doMovement(frameDeltaTime);
				obj->handleEvents(this);
				obj->passTime(frameDeltaTime);
				
				

				if(obj->isDestroyed())
					garbage.push_back(obj);
			}

			// clean up
			for(std::vector<CSceneObject *>::iterator iter = garbage.begin(); iter != garbage.end(); iter++) {
				obj = *iter;
				this->removeObject(obj);
				delete obj;
			}
		} 

		this->mCameraControl->update(frameDeltaTime);

		this->mVideoDriver->beginScene(true, true, SColor(0,0,0,0));
		this->mSceneManager->drawAll();
#ifdef _DEBUG
		this->mPhysxManager->renderDebugData(SColor(0,0,0,255));
#endif
		this->mHudControl->drawAll();
		this->mGuiManager->drawAll();

		this->mVideoDriver->endScene();
		this->mEventManager->update();
	}
	this->mEventManager->drop();
	this->mDevice->drop();
}
void CGameManager::pause(void) {
	this->mState = E_STATE::Paused;
}
void CGameManager::unpause(void) {
	this->mState = E_STATE::Running;
}
void CGameManager::go(void) {
#ifdef _DEBUG
	TestLoad(this);
#endif
	this->mState = E_STATE::Running;
	this->MainLoop();
}
void CGameManager::prepareDevice(E_DRIVER_TYPE deviceType, dimension2d<s32> dimensions, u32 depth, bool fullscreen, bool stencil, bool vsync) {
	SIrrlichtCreationParameters deviceParams;

	deviceParams.DriverType = deviceType;
	deviceParams.WindowSize = dimensions;
	deviceParams.Bits = depth;
	deviceParams.Fullscreen = fullscreen;
	deviceParams.Stencilbuffer = stencil;
	deviceParams.Vsync = vsync;
	deviceParams.EventReceiver = this->mEventManager;
	deviceParams.AntiAlias = true;

	if((this->mDevice = irr::createDeviceEx(deviceParams)) == NULL) {
		throw -1;
	}
	
	this->mVideoDriver = this->mDevice->getVideoDriver();
	this->mSceneManager = this->mDevice->getSceneManager();
	this->mGuiManager = this->mDevice->getGUIEnvironment();

	this->mDevice->setWindowCaption(L"Honor");
	this->mCameraControl = new CCameraControl();

	
}


CGameManager::CGameManager(void) {
	this->mDevice = NULL;
	this->mVideoDriver = NULL;
	this->mSceneManager = NULL;
	this->mGuiManager = NULL;

	this->mEventManager = createEventManager();
	
	this->prepareDevice(EDT_DIRECT3D9, dimension2d<s32>(1280, 1024), 32, false, false, false);

	this->mHudControl = new CHUDControl(this);

	SSceneDesc sceneDesc;
	this->mPhysxManager = createPhysxManager(this->mDevice, sceneDesc);
	this->mPhysxManager->setGravity(vector3df(0,0,0));
	this->mCollisionDispatcher = CCollisionDispatcher::getInstance();
	CObjectTypesContainer::loadTypes();
}

CGameManager::~CGameManager(void) {
}
//----- Accessors ----//
void CGameManager::addObject(CSceneObject *obj) {
	if(obj == NULL)
		return;

	this->mObjectList.insert(OBJINTPAIR(obj->getID(), obj));
}
CSceneObject *CGameManager::getObject(s32 id) { 
	CSceneObject *ptr;
	OBJINTMAP::iterator iter;

	for(iter = this->mObjectList.begin();iter!=this->mObjectList.end();iter++) {
		ptr = iter->second;
		if(ptr->getID() == id) 
			return ptr;
	}

	return NULL;
} 
void CGameManager::removeObject(s32 id) { 
	CSceneObject *ptr;
	OBJINTMAP::iterator iter;

	for(iter = this->mObjectList.begin();iter!=this->mObjectList.end();iter++) {
		ptr = iter->second;
		if(ptr->getID() == id) {
			this->mObjectList.erase(iter);
			return;
		}
	}
} 
void CGameManager::removeObject(CSceneObject *obj) { 
	CSceneObject *ptr;
	OBJINTMAP::iterator iter;

	for(iter = this->mObjectList.begin();iter!=this->mObjectList.end();iter++) {
		ptr = iter->second;

		if(ptr == obj) {
			this->mObjectList.erase(iter);
			return;
		}
	}
} 